package org.nonsense;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.DateFormat;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.concurrent.TimeoutException;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.Mixer;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.TargetDataLine;
import javax.swing.JComponent;
import javax.swing.JOptionPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;

import org.apache.commons.lang.time.DurationFormatUtils;


public class Presenter implements ActionListener, FocusListener {

	
	public Mixer.Info[] mixerInfo = AudioSystem.getMixerInfo();
	private RecorderModel recorderModel = new RecorderModel();
	private AudioRecorderFrame arf;
	private Presenter presenter;
	private CaptureThread captureThread = null;

	public static void main(String[] args) {
		Presenter presenter = new Presenter();
		presenter.initPresenter();
	}

	
	public Presenter()	{
		
	}
	

	public void initPresenter()	{
		presenter = this;
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				arf = new AudioRecorderFrame();
				arf.setLocationRelativeTo(null);
				arf.setVisible(true);

				// populate view with some info
				arf.updateMixerComboBox(mixerInfo);
				arf.setActionListener(presenter);
				arf.setNoPlaybackMode();
				arf.getFilePrefixTextField().setText(recorderModel.getOutputFilePrefix());
				arf.setOutputTypes(recorderModel.recordingTypes);
			}
		});
		
		loop();
	}

	public boolean daySelectionMatchesToday()	{
		String selectedDays = recorderModel.getRecordOnDays();

		// if today only, or every day... call it good right here
		if( selectedDays.equals(RecorderModel.recordingDays[RecorderModel.TODAY_ONLY]) ||
				selectedDays.equals(RecorderModel.recordingDays[RecorderModel.EVERY_DAY]) )	{
			return true;
		}
		else	{	// otherwise dig a bit deeper...
			System.out.println("Recording day selection not yet implemented, please choose a different recording day option.");
		}
		
		
		return false;
	}
		
	// method used to manage timed recordings...
	private void loop()	{
		SimpleDateFormat sdf = new SimpleDateFormat("mm/dd/yyyy hh:mm:ss");

		while( true )	{
			Calendar cal = Calendar.getInstance();
			String currentTime = sdf.format(cal.getTime());

			// if we're already recording...
			if( recorderModel.isTimedRecordingInProgress() )	{
				String endTime = recorderModel.getRecordEndTime() + ":00";
				if( endTime.equals(currentTime) )	{
					captureThread.stopCapture();
					arf.setNoPlaybackMode();
				}
				
			}// else if timed recording is enabled...
			else if( recorderModel.isTimedRecordingEnabled() )	{
				String startTime = "";
				
				if( daySelectionMatchesToday() )	{
					startTime = recorderModel.getRecordStartTime()+":00";					
					
					if( startTime.equals(currentTime) )	{
						// Start a recording session
						recorderModel.toggleTimedRecordingInProgress();	// set in progress
						captureAudio();
						arf.setCaptureMode();
					}
					
				}
				else	{
					System.out.println("Its not our day...");
				}
				
				
//				System.out.println("cal.getTime() = " + Calendar.getInstance().getTime());
				try	{
					// add seconds formatting required to detect when the moment happens
					startTime += ":00";

					Calendar startTimeCal = recorderModel.getRecordStartTimeCal();
					
					
					SimpleDateFormat dateFormat = new SimpleDateFormat();					
					dateFormat.applyPattern("mm/dd/yyyy hh:mm:ss");
				    Date dateOne = dateFormat.parse(currentTime);
				    Date dateTwo = dateFormat.parse(startTimeCal.get(Calendar.MONTH) + "/" + startTimeCal.get(Calendar.DAY_OF_MONTH)+"/"+startTimeCal.get(Calendar.YEAR)+" "+ startTimeCal.get(Calendar.HOUR_OF_DAY)+":"+startTimeCal.get(Calendar.MINUTE)+":"+startTimeCal.get(Calendar.SECOND));										

				    
				    if( dateTwo.compareTo(dateOne) == -1 )	{	// recording time has passed, check for next recording time
				    	dateTwo = findNextRecordingTime(dateTwo, recorderModel.getRecordOnDays());
				    }
				    
				    
					String nextRecordingIn = DurationFormatUtils.formatDurationWords(dateTwo.getTime() - dateOne.getTime(), true, true);					
					nextRecordingIn=nextRecordingIn.replace("days", "d");
					nextRecordingIn=nextRecordingIn.replace("day", "d");
					nextRecordingIn=nextRecordingIn.replace("hours", "h");
					nextRecordingIn=nextRecordingIn.replace("hour", "h");
					nextRecordingIn=nextRecordingIn.replace("minutes", "m");
					nextRecordingIn=nextRecordingIn.replace("minute", "m");
					nextRecordingIn=nextRecordingIn.replace("seconds", "s");
					nextRecordingIn=nextRecordingIn.replace("second", "s");
					
					arf.getNextRecordingTimeLabel().setText(nextRecordingIn);
					
				} catch (NumberFormatException e)	{
					e.printStackTrace();
				} catch (ParseException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				
				
				// TODO update the time until next session label
			}

		
			try	{	// Throttle the loop
				Thread.sleep(400);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}

		}
		

	}
	
	private Date findNextRecordingTime(Date dateTwo, String recordOnDays) {
		Calendar nextRecordingDate = null;

		nextRecordingDate = Calendar.getInstance();
		nextRecordingDate.setTime(dateTwo);

		
		if( recordOnDays.equals(RecorderModel.recordingDays[RecorderModel.EVERY_DAY]) )	{
			nextRecordingDate.add(Calendar.DATE, 1);
		}
	
		
		
		return nextRecordingDate.getTime();
	}


	// This method captures audio input from a mixer
	// and saves it in a ByteArrayOutputStream object.
	private void captureAudio() {
		
		if( recorderModel.isBatchCommandEnabled() )	{
			try {
				
				FileWriter batch = new FileWriter("temp.bat");
				BufferedWriter bw = new BufferedWriter(batch);			
				bw.write(recorderModel.getBatchFile());
				bw.close();
				batch.close();
				
				String userDir = System.getProperty("user.dir");
				System.out.println("User.dir: " + userDir);
//				String defaultBatchString = "C:\\Users\\derek\\AppData\\Local\\Google\\Chrome\\Application\\chrome.exe http://www.iheart.com/live/177/?autoplay=true";
	
				Process p = Runtime.getRuntime().exec(arf.getBatchCommandTextArea().getText());
				InputStream in = p.getInputStream();
				OutputStream out = p.getOutputStream();
				InputStream err = p.getErrorStream();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
		try{
			
			// Get everything set up for capture
			AudioFormat audioFormat = recorderModel.getAudioFormat();

			DataLine.Info dataLineInfo = new DataLine.Info(TargetDataLine.class, audioFormat);

			// Get a TargetDataLine on the selected
			// mixer.
			
			Mixer mixer = AudioSystem.getMixer(recorderModel.mixerInfo[recorderModel.getMixer()]);
						
			TargetDataLine targetDataLine = (TargetDataLine)mixer.getLine(dataLineInfo);
			
			recorderModel.setTargetDataLine(targetDataLine);	// we'll need this elsewhere...

			// Create a thread to capture the TargetDataLine
			// data and start it running. It will run
			// until the Stop button is clicked.
			captureThread = new CaptureThread(recorderModel);
			captureThread.start();
//			p.destroy();
		} catch (LineUnavailableException e)	{
			JOptionPane.showMessageDialog(arf, "Selected Mixer is unavailable, please choose a different mixer.");
			e.printStackTrace();
		} catch (IllegalArgumentException e)	{
			JOptionPane.showMessageDialog(arf, "Selected Mixer isn't compatible with the options you've selected.");
			e.printStackTrace();
		}
		
	}
	

	
	@Override
	public void actionPerformed(ActionEvent arg0) {

		if( arg0.getActionCommand() == "recordButton" )	{
			arf.setCaptureMode();
			captureAudio();
		}
		else if( arg0.getActionCommand() == "stopRecordButton" )	{
			arf.setIdleMode();
			captureThread.stopCapture();
		}
		else if( arg0.getActionCommand() == "playbackButton" )	{
//			arf.setPlaybackMode();
			JOptionPane.showMessageDialog(null, "Playback was removed and yet to be re-added.");
//			playAudio();
		}
		else if( arg0.getActionCommand() == "mixerSelectComboBox" )	{
			int index = arf.getMixerSelectComboBox().getSelectedIndex();
			recorderModel.setMixer(index);
		}
		else if( arg0.getActionCommand() == "sampleRateComboBox" )	{
			try	{
				int sampleRate = Integer.parseInt((String)arf.getSampleRateComboBox().getSelectedItem());
				recorderModel.setSampleRate(sampleRate);
			}
			catch (NumberFormatException e)	{
				e.printStackTrace();
			}
		}
		else if( arg0.getActionCommand() == "channelCountComboBox" )	{
			try	{
				int channelCount = Integer.parseInt((String)arf.getChannelCountComboBox().getSelectedItem());
				recorderModel.setChannels(channelCount);
			}
			catch (NumberFormatException e)	{
				e.printStackTrace();
			}
		}
		else if( arg0.getActionCommand() == "bigEndianCheckBox" )	{
			recorderModel.toggleBigEndian();
		}
		else if( arg0.getActionCommand() == "sampleSizeComboBox" )	{
			try	{
				int sampleSize = Integer.parseInt((String)arf.getSampleSizeComboBox().getSelectedItem());
				recorderModel.setSampleSizeInBits(sampleSize);
			}
			catch (NumberFormatException e)	{
				e.printStackTrace();
			}
		}
		else if( arg0.getActionCommand() == "signedCheckBox" )	{
			recorderModel.toggleSigned();
		}
		else if( arg0.getActionCommand() == "startTimePMCheckBox" )	{
			recorderModel.toggleStartTimePM();
		}
		else if( arg0.getActionCommand() == "endTimePMCheckBox" )	{
			recorderModel.toggleEndTimePM();
		}
		else if( arg0.getActionCommand() == "timedRecordingDaysComboBox" )	{
			recorderModel.setRecordOnDays(arf.getTimedRecordingDaysComboBox().getSelectedIndex());
		}
		else if( arg0.getActionCommand() == "archiveDirectoryTextField" )	{
			System.out.println(arg0.getActionCommand() + ": Not implemented.");
		}
		else if( arg0.getActionCommand() == "browseArchiveDirectoryButton" )	{
			System.out.println(arg0.getActionCommand() + ": Not implemented.");
		}
		else if( arg0.getActionCommand() == "outputFormatComboBox" )	{
			recorderModel.setOutputFormat(arf.getOutputFormatComboBox().getSelectedIndex());
		}
		else if( arg0.getActionCommand() == "filePrefixTextArea" )	{
			recorderModel.setOutputFilePrefix(arf.getFilePrefixTextField().getText());
		}
		else if( arg0.getActionCommand() == "onOffTimedRecordingButton" )	{
			recorderModel.toggleOnOffTimedRecording();
			String buttonText;
			
			if( recorderModel.isTimedRecordingEnabled() )
				buttonText = "Enabled";
			else
				buttonText = "Disabled";

			arf.toggleTimedRecording(buttonText);
		}
		else if( arg0.getActionCommand() == "batchCommandEnabledCheckBox")	{
			recorderModel.toggleBatchCommandEnabled();
		}
	}


	@Override
	public void focusGained(FocusEvent arg0) {
		Object a = arg0.getComponent();
		
		if( a instanceof JTextField )	{
			JTextField jtf = (JTextField)a;
			jtf.selectAll();
		}
	}

    void displayMessage(String prefix, FocusEvent e) {
        System.out.println(prefix
                + (e.isTemporary() ? " (temporary):" : ":")
                + e.getComponent().getClass().getName()
                + "; Opposite component: "
                + (e.getOppositeComponent() != null ?
                    e.getOppositeComponent().getClass().getName() : "null"));

    }	

	@Override
	public void focusLost(FocusEvent arg0) {
		
		displayMessage("FocusLost: ", arg0);
		
		Object a = arg0.getComponent();
		
		if( a instanceof JTextField )	{
			JTextField jtf = (JTextField)a;
			String text = jtf.getText();
			
			if( jtf.getName().equals("startTimeTextField") )	{
				TimeInputVerifier tiv = new TimeInputVerifier();
				if( tiv.verify(jtf) )	{
					recorderModel.setRecordStartTime(arf.getStartTimeTextField().getText());
					System.out.println("RecorderModel.getStartTime: " + recorderModel.getRecordStartTime());
				}
			}
			else if( jtf.getName().equals("endTimeTextField") )	{
				TimeInputVerifier tiv = new TimeInputVerifier();
				if( tiv.verify(jtf) )	{
					recorderModel.setRecordEndTime(arf.getEndTimeTextField().getText());
					System.out.println("RecorderModel.getEndTime: " + recorderModel.getRecordEndTime());
				}
			}
			else if( jtf.getName().equals("filePrefixTextField") )	{
				recorderModel.setOutputFilePrefix(arf.getFilePrefixTextField().getText());
				System.out.println("RecorderModel.getOutputFilePrefix: " + recorderModel.getOutputFilePrefix());
			}
			else if( jtf.getName().equals("archiveDirectoryTextField") )	{
				recorderModel.setArchiveDirectory(arf.getArchiveDirectoryTextField().getText());
				System.out.println("RecorderModel.getArchiveDirectory: " + recorderModel.getArchiveDirectory());
			}
			

		}
		else if( a instanceof JTextArea )	{
			JTextArea jta = (JTextArea)a;
			if( jta.getName().equals("batchCommandTextArea") )	{
				recorderModel.setBatchFile(arf.getBatchCommandTextArea().getText());
				System.out.println("RecorderModel.batchFile: " + recorderModel.getBatchFile());
			}

		}
		
/*		
		*/
	}
	
}
